<!--
 * @Author: zhouyankai 4848494717@qq.com
 * @Date: 2025-07-01 17:01:07
 * @LastEditors: zhouyankai 4848494717@qq.com
 * @LastEditTime: 2025-07-03 17:26:57
 * @FilePath: \BusStopBoard.Appc:\Users\szfinest-11\Desktop\three.js手机端\threeJsPc\src\view\home\index.vue
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<template>
    <div class="container">
        <div ref="container" class="scene-container"></div>
    </div>
</template>

<script lang="ts" setup name="home">
// 引入three.js
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { onMounted, ref } from 'vue';
import { CSS2DObject, CSS2DRenderer } from 'three/addons/renderers/CSS2DRenderer.js';
import cdImage from '../../../public/image/cd.jpg'
import { Pathfinding, PathfindingHelper } from 'three-pathfinding';


const container = ref();


let scene: any = null
let labelRenderer: any = null; // 用于渲染标签的CSS2DRenderer
let points = []; // 存储起点和终点
let dijkstraNodes = []

// 点击事件处理
const raycaster = new THREE.Raycaster();
raycaster.params.Line.threshold = 0.01; // 设置点的阈值
const mouse = new THREE.Vector2();


onMounted(() => {
    // 初始化场景
    scene = new THREE.Scene();
    const axesHelper = new THREE.AxesHelper(200);
    axesHelper.position.set(0, 0, 0)
    scene.add(axesHelper)

    const camera = new THREE.PerspectiveCamera(
        40,
        container.value.clientWidth / container.value.clientHeight,
        0.1,
        1000
    );
    camera.position.set(0, 0, 600);
    camera.lookAt(0, 0, 0);

    initLights()

    // initMeshloor()

    const geometry = new THREE.PlaneGeometry(200, 200);
    const material = new THREE.MeshBasicMaterial({ color: '#fff' });
    const cube = new THREE.Mesh(geometry, material);
    cube.userData.isClickable = false; // 标记可点击对象
    // cube.position.set(0, 0, 0)
    // cube.rotation.x = -Math.PI / 2;
    cube.rotation.x = 0
    scene.add(cube);



    // for (let i = 0; i < 20; i++) {
    //     scene.add(createCilr());
    // }

    // 生成50个节点数据
    dijkstraNodes = createRandomEdges(generateRandomNodes(50, 200, 200));


    console.log(dijkstraNodes, '我是节点数据');

    for (let index = 0; index < dijkstraNodes.length; index++) {
        const element = dijkstraNodes[index];
        const geometry = new THREE.SphereGeometry(2)
        const material = new THREE.MeshBasicMaterial({
            color: Math.random() * 0xffffff,
        });

        const x = element.x - 100
        const y = element.y - 100

        const cube = new THREE.Mesh(geometry, material);
        cube.position.x = x
        cube.position.y = y
        cube.position.z = 2 / 2
        cube.userData.isClickable = true; // 标记可点击对象
        point.push({
            x: Math.floor(x) + 100,
            y: Math.floor(y) + 100,
        })
        scene.add(cube);
    }


    // 输出结果
    console.log(dijkstraNodes, '我打印出来了呢');


    // NavMesh()
    createRandomObstacle()

    console.log(point, '我是')

    // for (let index = 0; index < point.length; index++) {
    //     const { x, y } = point[index];
    //     grid[x][y] = 1
    // }

    console.log(grid, '障碍物网格2');

    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(container.value.clientWidth, container.value.clientHeight);
    renderer.setClearColor('#AAAAAA', 1.0);
    container.value.appendChild(renderer.domElement);

    // 创建用于渲染标签的渲染器
    labelRenderer = new CSS2DRenderer(); // 创建一个CSS2DRenderer渲染器
    // 设置渲染器的大小为窗口大小
    labelRenderer.setSize(container.value.clientWidth, container.value.clientHeight);
    // 设置渲染器的DOM元素的定位方式为绝对定位
    labelRenderer.domElement.style.position = 'absolute';
    // 设置渲染器的DOM元素的顶部偏移量为0像素
    labelRenderer.domElement.style.top = '0px';
    // 将渲染器的DOM元素添加到文档中
    document.body.appendChild(labelRenderer.domElement);

    //创建CSS2DRenderer渲染器
    const controls = new OrbitControls(camera, labelRenderer.domElement);
    controls.minDistance = 100;
    controls.maxDistance = 1000;
    controls.target.set(0, 0, 0); // 控制器焦点设为原点


    let isSettingStart = true; // 是否设置起点

    document.addEventListener('click', (event: { clientX: number; clientY: number }) => {
        // 计算鼠标标准化设备坐标
        mouse.x = (event.clientX / container.value.clientWidth) * 2 - 1;
        mouse.y = -(event.clientY / container.value.clientHeight) * 2 + 1;

        raycaster.setFromCamera(mouse, camera);

        // 获取raycaster直线和所有模型相交的数组集合
        const intersects = raycaster.intersectObjects(scene.children);

        console.log(intersects, '相交的点');

        if (intersects.length > 0) {
            const point = intersects[0].point;

            const clickedObj = intersects[0].object;

            console.log(clickedObj, '点击的对象');

            // 仅处理标记为可点击的对象
            if (clickedObj.userData.isClickable) {
                console.log(clickedObj.material, '点击了可点击对象');
                clickedObj.material.color.set('#f00');
            } else {
                const geometry = new THREE.SphereGeometry(2);
                const material = new THREE.MeshBasicMaterial({ color: isSettingStart ? 0xff0000 : 0x00ff00 });
                // 创建标记球体
                const marker = new THREE.Mesh(geometry, material);
                marker.position.copy(point);
                console.log(point, '点击的点坐标', point);
                scene.add(marker);
                isSettingStart = !isSettingStart;
                // 保存点对象
                points.push({
                    obj: marker,
                    position: point
                });
                // 添加标签
                if (points.length === 1) {
                    // createLabel(sphere, "起点");

                    // 创建地球的标题html
                    const earthDiv = document.createElement('div');
                    // 设置div类名
                    earthDiv.className = 'label';
                    // 设置div内的html
                    earthDiv.innerHTML = `
                    <div style="
                        background: rgba(0,0,0,0.7);
                        color: white;
                        padding: 5px 10px;
                        border-radius: 5px;
                        font-family: Arial;
                        white-space: nowrap;
                        font-size: 12px;
                    ">
                        起点
                    </div>
                `;

                    // 使用CSS2DObject将<div>元素转换为可在Three.js场景中渲染的对象
                    const earthLabel = new CSS2DObject(earthDiv);
                    // 设置地球标题对象在场景中的位置，X轴偏移量为地球半径的1.5倍
                    earthLabel.position.set(0, 0, 0);
                    earthLabel.center.set(0.5, 0.5); // 设置地球标题对象的中心点为顶部中心
                    marker.add(earthLabel); // 将地球标题对象添加到地球模型上
                } else {
                    // createLabel(sphere, "终点");
                    const earthDiv = document.createElement('div');
                    // 设置div类名
                    earthDiv.className = 'label';
                    // 设置div内的html
                    earthDiv.innerHTML = `
                    <div style="
                        background: rgba(0,0,0,0.7);
                        color: white;
                        padding: 5px 10px;
                        border-radius: 5px;
                        font-family: Arial;
                        white-space: nowrap;
                        font-size: 12px;
                    ">
                        终点
                    </div>
                `;

                    // 使用CSS2DObject将<div>元素转换为可在Three.js场景中渲染的对象
                    const earthLabel = new CSS2DObject(earthDiv);
                    // 设置地球标题对象在场景中的位置，X轴偏移量为地球半径的1.5倍
                    earthLabel.position.set(0, 0, 0);
                    earthLabel.center.set(0.5, 0.5); // 设置地球标题对象的中心点为顶部中心
                    marker.add(earthLabel); // 将地球标题对象添加到地球模型上
                    // 添加两点间的连线
                    createConnectionLine(points[0].position, points[1].position);
                }
            }
        }
        console.log(intersects, '我点击了呢')


    });


    // // 动画循环
    const animate = () => {
        requestAnimationFrame(animate);
        labelRenderer.render(scene, camera);
        renderer.render(scene, camera);
    };
    animate();
})


class DijkstraCoordinatePathfinder {
    constructor(nodes) {
        this.nodes = nodes;
        this.graph = this.buildGraph();
        this.nodeMap = this.buildNodeMap();
    }

    // 构建邻接表图结构
    buildGraph() {
        const graph = {};
        this.nodes.forEach(node => {
            graph[node.id] = node.edges.map(edge => ({
                target: edge.target,
                weight: edge.weight
            }));
        });
        return graph;
    }

    // 构建坐标到节点的映射
    buildNodeMap() {
        const map = new Map();
        this.nodes.forEach(node => {
            map.set(`${node.x},${node.y}`, node.id);
        });
        return map;
    }

    // 查找最近节点ID（欧氏距离）
    findNearestNodeId(x, y) {
        let minDistance = Infinity;
        let nearestId = null;

        for (const node of this.nodes) {
            const distance = Math.sqrt(Math.pow(node.x - x, 2) + Math.pow(node.y - y, 2));
            if (distance < minDistance) {
                minDistance = distance;
                nearestId = node.id;
            }
        }
        return nearestId;
    }

    // Dijkstra算法实现
    findShortestPathByCoords(startCoord, endCoord) {
        const startId = this.findNearestNodeId(startCoord.x, startCoord.y);
        const endId = this.findNearestNodeId(endCoord.x, endCoord.y);

        if (startId === null || endId === null) {
            throw new Error("起点或终点坐标无效");
        }

        const distances = {};
        const previous = {};
        const unvisited = new Set();
        const INFINITY = Number.MAX_SAFE_INTEGER;

        // 初始化距离和前驱节点
        this.nodes.forEach(node => {
            distances[node.id] = node.id === startId ? 0 : INFINITY;
            previous[node.id] = null;
            unvisited.add(node.id);
        });

        while (unvisited.size > 0) {
            // 获取当前最小距离节点
            let currentId = null;
            let minDistance = INFINITY;

            for (const nodeId of unvisited) {
                if (distances[nodeId] < minDistance) {
                    minDistance = distances[nodeId];
                    currentId = nodeId;
                }
            }

            // 终止条件：到达终点或无可达节点
            if (currentId === null || currentId === endId) break;
            unvisited.delete(currentId);

            // 更新邻居节点距离
            const neighbors = this.graph[currentId] || [];
            for (const neighbor of neighbors) {
                const alt = distances[currentId] + neighbor.weight;
                if (alt < distances[neighbor.target]) {
                    distances[neighbor.target] = alt;
                    previous[neighbor.target] = currentId;
                }
            }
        }

        // 回溯构建路径
        const path = [];
        let currentId = endId;
        while (currentId !== null) {
            const node = this.nodes.find(n => n.id === currentId);
            path.unshift({ x: node.x, y: node.y });
            currentId = previous[currentId];
        }

        return {
            path: path,
            distance: distances[endId],
            startNodeId: startId,
            endNodeId: endId
        }
    }
}



function createConnectionLine(start, end) {

    // const zone = 'level'; // 定义导航网格区域名称
    // const starts = new THREE.Vector3(start.x, start.y, 0); // 起 点
    // const ends = new THREE.Vector3(end.x, end.y, 0); // 起 点
    // console.log(zone, starts, ends, '我是起点')
    // const path = findPaths(zone, starts, ends);

    // console.log(path, '我是路径数据');

    // const groupID = pathfinding.getGroup(zone, starts);
    // console.log(groupID, '我是分组ID');

    // const near_start = pathfinding.getClosestNode(starts, zone, groupID, true);

    // const near_end = pathfinding.getClosestNode(ends, zone, groupID, true);

    // console.log(near_start, near_end, '我是起点附近最近的点');



    // // const path = pathfinding.findPath(near_start, near_end, zone, groupID);

    // // console.log(path, '我是路径数据');


    // if (path) { // 如果出发坐标到终点坐标都有可达路线（start - end）
    //     return path;
    // } else { // 终点如果不可达，找终点附近最近的点（start - near_end）
    //     const near_end = pathfinding.getClosestNode(ends, zone, pathfinding.getGroup(zone, ends)).centroid;
    //     console.log(near_end, '我是终点附近最近的点');
    //     const start_to_near_end_path = findPaths(zone, starts, near_end);

    //     if (start_to_near_end_path) {
    //         console.log(start_to_near_end_path, '我是路径1');
    //         return start_to_near_end_path;
    //     } else { // （卡死情况）如果出发坐标都不可取，则找出发位置最近的点，再到终点（near_start - end）



    //         const near_start = pathfinding.getClosestNode(starts, zone, pathfinding.getGroup(zone, starts)).centroid;
    //         const near_start_to_end_path = findPaths(zone, near_start, near_end);

    //         console.log(near_start, '我是出发位置最近的点');
    //         console.log(near_start_to_end_path, '我是终点位置最近的点');

    //         if (near_start_to_end_path) {
    //             console.log(near_start_to_end_path, '我是路径2');
    //             return near_start_to_end_path;
    //         } else { // （卡死情况）如果出发坐标和终点坐标都不可取，则从找出两个坐标最近的点，再到终点（near_start - near_end）
    //             return findPaths(zone, near_start, near_end);
    //         }
    //     }
    // }



    // console.log(start, end, '我是起点和终点');

    // // 定义起点终点
    // const hikingStart = { x: Math.floor(start.x) + 100, y: Math.floor(start.y) + 100 }
    // const mountainTop = { x: Math.floor(end.x) + 100, y: Math.floor(end.y) + 100 }


    // const pathfinder = new DijkstraCoordinatePathfinder(dijkstraNodes);
    // const result = pathfinder.findShortestPathByCoords(hikingStart, mountainTop);
    // console.log('最短路径节点序列:', result.path);
    // console.log('总距离:', result.distance);

    // const path = findPath(
    //     { x: Math.floor(start.x) + 100, y: Math.floor(start.y) + 100 },
    //     { x: Math.floor(end.x) + 100, y: Math.floor(end.y) + 100 },
    // );

    // const points = [];
    // result.path.forEach(p => {
    //     points.push(p.x - 100, p.y - 100, 0); // z轴默认为0
    // });

    const geometry = new THREE.BufferGeometry();
    geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(points), 3));
    const material = new THREE.LineBasicMaterial({ color: 0x00ff00 });
    const line = new THREE.Line(geometry, material);
    scene.add(line);


    // console.log(path, '我是路径');

}

function findPaths(zone, start, end) {
    return pathfinding.findPath(start, end, zone, pathfinding.getGroup(zone, start));
}


function initLights() {
    // 添加环境光
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
    scene.add(ambientLight);

    // 添加平行光
    // const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    // directionalLight.position.set(10, 10, 10);
    // scene.add(directionalLight);
}

// 初始化大地
function initMeshloor() {
    const textureLoader: any = new THREE.TextureLoader();
    // const texLoader = textureLoader.load("image/cd.jpg");

    // .load()方法加载图像，返回一个纹理对象Texture
    const texture = textureLoader.load('image/cd.jpg');

    console.log('纹理贴图加载中', cdImage);

    const material = new THREE.MeshLambertMaterial({
        // 设置纹理贴图：Texture对象作为材质map属性的属性值
        map: texture,//map表示材质的颜色贴图属性
    });

    const geometry = new THREE.PlaneGeometry(2000, 2000); //矩形平面
    const mesh = new THREE.Mesh(geometry, material); //纹理贴图网格模型对象
    // mesh.position.y = -0;
    // mesh.rotation.x = - Math.PI / 2;
    mesh.position.y = - Math.PI / 2;
    scene.add(mesh);
    console.log('纹理贴图加载完成', cdImage);
}

// 添加障碍物
function obstacle() {

}

// 奶茶店名称库
const teaShopNames = [
    "茶言蜜语", "奶香小筑", "果茶星球", "甜沫时光", "云朵茶坊",
    "蜜雪倾城", "茶里茶气", "泡泡鲜茶", "糖半仙", "茶颜观色"
];


const floorCoordinates = [
    [82, -37],
    [-15, 68],
    [45, -89],
    [-76, 53],
    [91, 12]
];



// 随机生成障碍物函数
function createRandomObstacle() {

    for (let index = 0; index < 5; index++) {
        const size = Math.random() * 20 + 10; // 10-30的随机大小
        const zheignt = Math.random() * 18; // 10-30的随机大小
        const geometry = new THREE.BoxGeometry(size, size, zheignt);
        const material = new THREE.MeshBasicMaterial({
            color: Math.random() * 0xffffff,
        });

        const cube = new THREE.Mesh(geometry, material);
        cube.position.x = floorCoordinates[index][0]
        cube.position.y = floorCoordinates[index][1]
        cube.position.z = zheignt / 2
        cube.userData.isClickable = true; // 标记可点击对象

        // 创建地球的标题html
        const earthDiv = document.createElement('div');
        // 设置div类名
        earthDiv.className = 'label';
        // 设置div内的html
        earthDiv.innerHTML = `
        <div style="
            background: rgba(0,0,0,0.7);
            color: white;
            padding: 5px 10px;
            border-radius: 5px;
            font-family: Arial;
            white-space: nowrap;
            font-size: 12px;
        ">
            ${teaShopNames[Math.floor(Math.random() * teaShopNames.length)]}
        </div>
    `;

        // 使用CSS2DObject将<div>元素转换为可在Three.js场景中渲染的对象
        const earthLabel = new CSS2DObject(earthDiv);
        // 设置地球标题对象在场景中的位置，X轴偏移量为地球半径的1.5倍
        earthLabel.position.set(0, 0, 0);
        earthLabel.center.set(0.5, 0.5); // 设置地球标题对象的中心点为顶部中心
        cube.add(earthLabel); // 将地球标题对象添加到地球模型上
        // cube.position.x = (Math.random() - 0.5) * 180; // -90到90范围内
        // cube.position.z = (Math.random() - 0.5) * 180; // -90到90范围内
        // cube.position.y = size / 2; // 确保立方体底部接触平台
        scene.add(cube);

    }
}

const point = []

function createCilr() {
    const zheignt = Math.random() * 18; // 10-30的随机大小
    const geometry = new THREE.SphereGeometry(2)
    const material = new THREE.MeshBasicMaterial({
        color: Math.random() * 0xffffff,
    });

    const x = (Math.random() - 0.5) * 180
    const y = (Math.random() - 0.5) * 180

    const cube = new THREE.Mesh(geometry, material);
    cube.position.x = x
    cube.position.y = y
    cube.position.z = zheignt / 2
    cube.userData.isClickable = true; // 标记可点击对象
    point.push({
        x: Math.floor(x) + 100,
        y: Math.floor(y) + 100,
    })
    return cube;
}

let pathfinding = null
// 生成导航网格
function NavMesh() {


    // 创建随机几何体
    const geometry = new THREE.BufferGeometry();
    const vertices = [];
    const size = 200;

    // 生成5-10个随机顶点
    const vertexCount = Math.floor(Math.random() * 6) + 5;
    for (let i = 0; i < vertexCount; i++) {
        vertices.push(
            Math.random() * size - size / 2,  // x (-100到100)
            Math.random() * size - size / 2,  // y (-100到100)
            0                               // z (平面)
        );
    }

    // const vertices = new Float32Array([
    //     0, 0, 0, //顶点1坐标
    //     100, 0, 0, //顶点2坐标
    //     0, 100, 0, //顶点3坐标
    //     0, 0, 0, //顶点4坐标
    //     0, 0, 0, //顶点5坐标
    //     60, 0, 0, //顶点6坐标
    // ]);

    console.log(vertices, '我是顶点数据');
    // 闭合形状
    vertices.push(vertices[0], vertices[1], vertices[2]);

    // const indices = [
    //     0, 1, 2,
    //     2, 3, 0,
    // ];

    // geometry.setIndex(indices);
    geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));

    // 创建材质并添加到场景
    const material = new THREE.MeshBasicMaterial({ color: 'red' });
    const line = new THREE.Mesh(geometry, material);

    line.position.z = 2

    scene.add(line);

    console.log(line, '我笑死了额')

    // 初始化寻路系统
    pathfinding = new Pathfinding();
    // const helper = new PathfindingHelper();  // 可视化辅助工具

    console.log(pathfinding, '我是calsas')
    // pathfinding.createZone('fllorone', pathfinding.createZone(line.geometry))

    pathfinding.setZoneData('level', Pathfinding.createZone(geometry));

    // const groupID = pathfinding.getGroup('level', position);
    // const node = pathfinding.getClosestNode(position, 'level', groupID);




    // const size = Math.random() * 20 + 10; // 10-30的随机大小
    // const zheignt = Math.random() * 18; // 10-30的随机大小
    // const geometry = new THREE.BoxGeometry(size, size, zheignt);
    // const material = new THREE.MeshBasicMaterial({
    //     color: Math.random() * 0xffffff,
    // });



    // const cube = new THREE.Mesh(geometry, material);
    // cube.position.x = (Math.random() - 0.5) * 180
    // cube.position.y = (Math.random() - 0.5) * 180
    // cube.position.z = zheignt / 2
    // cube.userData.isClickable = true; // 标记可点击对象
    // scene.add(cube);
}




// 生成200x200平面内的50个随机节点
function generateRandomNodes(count, maxX, maxY) {
    const nodes = [];
    for (let i = 0; i < count; i++) {
        nodes.push({
            id: i,
            x: Math.floor(Math.random() * maxX),
            y: Math.floor(Math.random() * maxY),
            edges: []
        });
    }
    return nodes;
}

// 为节点创建随机连接关系
function createRandomEdges(nodes, maxEdgesPerNode = 4) {
    nodes.forEach(node => {
        const edgeCount = Math.floor(Math.random() * maxEdgesPerNode) + 1;
        const connectedNodes = new Set();

        while (connectedNodes.size < edgeCount) {
            const randomNode = nodes[Math.floor(Math.random() * nodes.length)];
            if (randomNode.id !== node.id && !connectedNodes.has(randomNode.id)) {
                const distance = Math.sqrt(
                    Math.pow(node.x - randomNode.x, 2) +
                    Math.pow(node.y - randomNode.y, 2)
                );
                node.edges.push({
                    target: randomNode.id,
                    weight: Math.floor(distance)
                });
                connectedNodes.add(randomNode.id);
            }
        }
    });
    return nodes;
}




// var length = 200;
// var ws = 2;
// const graph = []

const gridSize = 200;
const grid = Array(gridSize).fill().map(() => Array(gridSize).fill(0));

console.log(grid, '障碍物网格');

// 模型坐标转网格索引函数
function positionToGridIndex(xStart, xEnd, yStart, yEnd, size) {
    // const normalized = (pos + 200 / 2) / 200;

    const rectanglePoints = [
        { x: xStart + 100, y: 2 },
        { x: 5, y: 2 },
        { x: 5, y: 5 },
        { x: 2, y: 5 }
    ];

    console.log(xStart, xEnd, yStart, yEnd, size, '归一化坐标');

    // 找出矩形的边界
    let minX = Math.min(points[0].x, points[1].x, points[2].x, points[3].x);
    let maxX = Math.max(points[0].x, points[1].x, points[2].x, points[3].x);
    let minY = Math.min(points[0].y, points[1].y, points[2].y, points[3].y);
    let maxY = Math.max(points[0].y, points[1].y, points[2].y, points[3].y);

    // 填充矩形区域
    for (let y = minY; y <= maxY; y++) {
        for (let x = minX; x <= maxX; x++) {
            if (y >= 0 && y < grid.length && x >= 0 && x < grid[0].length) {
                grid[y][x] = 1;
            }
        }
    }


    // return Math.floor(normalized * gridSize);
}

// // 初始化障碍物
// function initGrid() {
//     for (var i = 0; i < length / ws; i++) {
//         var nodeRow = [];
//         for (var j = 0; j < length / ws; j++) {
//             nodeRow.push(0);
//         }
//         graph.push(nodeRow);
//     }
//     console.log(graph, '障碍物网格');
// }

// initGrid()

// 使用a*算法寻路

function heuristic(pos0, pos1) {
    return Math.abs(pos1.x - pos0.x) + Math.abs(pos1.y - pos0.y);
}

function findPath(start, end) {
    const openSet = [start];
    const closedSet = [];
    const cameFrom = {};
    const gScore = { [`${start.x},${start.y}`]: 0 };
    const fScore = { [`${start.x},${start.y}`]: heuristic(start, end) };

    while (openSet.length > 0) {
        let current = openSet.reduce((a, b) =>
            fScore[`${a.x},${a.y}`] < fScore[`${b.x},${b.y}`] ? a : b);

        if (current.x === end.x && current.y === end.y) {
            return reconstructPath(cameFrom, current);
        }

        openSet.splice(openSet.indexOf(current), 1);
        closedSet.push(current);

        for (const neighbor of getNeighbors(current)) {
            if (closedSet.some(n => n.x === neighbor.x && n.y === neighbor.y)) continue;

            const tentativeGScore = gScore[`${current.x},${current.y}`] + 1;
            const neighborKey = `${neighbor.x},${neighbor.y}`;

            if (!openSet.some(n => n.x === neighbor.x && n.y === neighbor.y)) {
                openSet.push(neighbor);
            } else if (tentativeGScore >= gScore[neighborKey]) {
                continue;
            }

            cameFrom[neighborKey] = current;
            gScore[neighborKey] = tentativeGScore;
            fScore[neighborKey] = gScore[neighborKey] + heuristic(neighbor, end);
        }
    }
    return []; // 无路径
}

function getNeighbors(node) {
    const neighbors = [];
    const directions = [
        { x: 0, y: 1 }, { x: 1, y: 0 },
        { x: 0, y: -1 }, { x: -1, y: 0 }
    ];

    for (const dir of directions) {
        const x = node.x + dir.x;
        const y = node.y + dir.y;
        if (x >= 0 && x < grid.length &&
            y >= 0 && y < grid[0].length &&
            grid[x][y] === 0) {
            neighbors.push({ x, y });
        }
    }
    return neighbors;
}

function reconstructPath(cameFrom, current) {
    const path = [current];
    while (cameFrom[`${current.x},${current.y}`]) {
        current = cameFrom[`${current.x},${current.y}`];
        path.unshift(current);
    }
    return path;
}


</script>

<style scoped>
.container {
    width: 100%;
    height: 100vh;
}

.scene-container {
    width: 100%;
    height: 100vh;
}
</style>
